/*
 * SPDX-FileCopyrightText: 2023-2024 Andrew Gunnerson
 * SPDX-License-Identifier: GPL-3.0-only
 */

package com.sanji.audio_test

import android.Manifest
import android.content.Context
import android.net.Uri
import android.os.Parcelable
import android.provider.ContactsContract
import androidx.annotation.RequiresPermission
import androidx.core.database.getLongOrNull
import androidx.core.database.getStringOrNull
import com.sanji.audio_test.extension.asSequence
import com.sanji.audio_test.output.PhoneNumber
import kotlinx.parcelize.Parcelize
import java.io.IOException

/**
 * 联系人基本信息查询列投影
 * 包含联系人的lookup key和显示名称
 */
private val PROJECTION = arrayOf(
    ContactsContract.PhoneLookup.LOOKUP_KEY,
    ContactsContract.PhoneLookup.DISPLAY_NAME,
)

/**
 * 群组成员关系查询列投影
 * 包含群组的行ID和源ID
 */
private val PROJECTION_GROUP_MEMBERSHIP = arrayOf(
    ContactsContract.CommonDataKinds.GroupMembership.GROUP_ROW_ID,
    ContactsContract.CommonDataKinds.GroupMembership.GROUP_SOURCE_ID,
)

/**
 * 联系人群组信息查询列投影
 * 包含群组的ID、源ID、标题和账户名称
 */
private val CONTACT_GROUP_PROJECTION = arrayOf(
    ContactsContract.Groups._ID,
    ContactsContract.Groups.SOURCE_ID,
    ContactsContract.Groups.TITLE,
    ContactsContract.Groups.ACCOUNT_NAME,
)

/**
 * 联系人信息数据类
 * @param lookupKey 联系人的lookup key，用于唯一标识联系人
 * @param displayName 联系人的显示名称
 */
data class ContactInfo(
    val lookupKey: String,
    val displayName: String,
)

/**
 * 群组查找接口
 * 用于通过不同方式查找联系人群组
 */
sealed interface GroupLookup {
    /**
     * 通过数据库行ID查找群组
     * @param id 群组的数据库行ID
     */
    data class RowId(val id: Long): GroupLookup

    /**
     * 通过源ID查找群组
     * @param id 群组的源ID
     */
    data class SourceId(val id: String): GroupLookup
}

/**
 * 联系人群组信息数据类
 * @param rowId 群组的数据库行ID
 * @param sourceId 群组的源ID（可为空）
 * @param title 群组标题
 * @param accountName 账户名称（可为空）
 */
@Parcelize
data class ContactGroupInfo(
    val rowId: Long,
    val sourceId: String?,
    val title: String,
    val accountName: String?,
) : Parcelable

/**
 * 根据电话号码查询联系人信息
 * @param context 应用上下文
 * @param number 电话号码
 * @param block 处理联系人信息序列的回调函数
 * @return 回调函数的返回值
 */
@RequiresPermission(Manifest.permission.READ_CONTACTS)
fun <R> withContactsByPhoneNumber(
    context: Context,
    number: PhoneNumber,
    block: (Sequence<ContactInfo>) -> R,
): R {
    val rawNumber = number.toString()

    // 使用与InCallUI的PhoneNumberHelper.isUriNumber()相同的启发式方法
    val numberIsSip = rawNumber.contains("@") || rawNumber.contains("%40")

    val uri = ContactsContract.PhoneLookup.ENTERPRISE_CONTENT_FILTER_URI.buildUpon()
        .appendPath(rawNumber)
        .appendQueryParameter(
            ContactsContract.PhoneLookup.QUERY_PARAMETER_SIP_ADDRESS,
            numberIsSip.toString())
        .build()

    return withContactsByUri(context, uri, block)
}

/**
 * 根据lookup key获取联系人信息
 * @param context 应用上下文
 * @param lookupKey 联系人的lookup key
 * @return 联系人信息，如果未找到则返回null
 */
@RequiresPermission(Manifest.permission.READ_CONTACTS)
fun getContactByLookupKey(context: Context, lookupKey: String): ContactInfo? {
    val uri = ContactsContract.Contacts.CONTENT_LOOKUP_URI.buildUpon()
        .appendPath(lookupKey)
        .build()

    return withContactsByUri(context, uri) { it.firstOrNull() }
}

/**
 * 根据URI查询联系人信息
 * @param context 应用上下文
 * @param uri 查询URI
 * @param block 处理联系人信息序列的回调函数
 * @return 回调函数的返回值
 */
fun <R> withContactsByUri(context: Context, uri: Uri, block: (Sequence<ContactInfo>) -> R): R {
    val cursor = context.contentResolver.query(uri, PROJECTION, null, null, null)
        ?: throw IOException("查询返回空游标: $uri")

    return cursor.use {
        val indexLookupKey = cursor.getColumnIndexOrThrow(ContactsContract.PhoneLookup.LOOKUP_KEY)
        val indexName = cursor.getColumnIndexOrThrow(ContactsContract.PhoneLookup.DISPLAY_NAME)

        block(cursor.asSequence().map {
            ContactInfo(cursor.getString(indexLookupKey), cursor.getString(indexName))
        })
    }
}

/**
 * 查询联系人的群组成员关系
 * @param context 应用上下文
 * @param lookupKey 联系人的lookup key
 * @param block 处理群组查找信息序列的回调函数
 * @return 回调函数的返回值
 */
@RequiresPermission(Manifest.permission.READ_CONTACTS)
fun <R> withContactGroupMemberships(
    context: Context,
    lookupKey: String,
    block: (Sequence<GroupLookup>) -> R,
): R {
    val selection = buildString {
        append(ContactsContract.CommonDataKinds.GroupMembership.LOOKUP_KEY)
        append(" = ? AND ")
        append(ContactsContract.CommonDataKinds.GroupMembership.MIMETYPE)
        append(" = ?")
    }
    val selectionArgs = arrayOf(
        lookupKey,
        ContactsContract.CommonDataKinds.GroupMembership.CONTENT_ITEM_TYPE,
    )

    val cursor = context.contentResolver.query(
        ContactsContract.Data.CONTENT_URI,
        PROJECTION_GROUP_MEMBERSHIP,
        selection,
        selectionArgs,
        null,
    ) ?: throw IOException("查询返回空游标")

    return cursor.use {
        val indexRowId = cursor.getColumnIndexOrThrow(
            ContactsContract.CommonDataKinds.GroupMembership.GROUP_ROW_ID)
        val indexSourceId = cursor.getColumnIndexOrThrow(
            ContactsContract.CommonDataKinds.GroupMembership.GROUP_SOURCE_ID)

        block(cursor.asSequence().mapNotNull {
            cursor.getLongOrNull(indexRowId)?.let { GroupLookup.RowId(it) }
                ?: cursor.getStringOrNull(indexSourceId)?.let { GroupLookup.SourceId(it) }
        })
    }
}

/**
 * 根据群组ID获取联系人群组信息
 * @param context 应用上下文
 * @param id 群组查找信息
 * @return 联系人群组信息，如果未找到则返回null
 */
@RequiresPermission(Manifest.permission.READ_CONTACTS)
fun getContactGroupById(context: Context, id: GroupLookup): ContactGroupInfo? {
    var selectionArgs: Array<String>? = null
    val selection = buildString {
        when (id) {
            is GroupLookup.RowId -> {
                append(ContactsContract.Groups._ID)
                append(" = ")
                append(id.id)
            }
            is GroupLookup.SourceId -> {
                append(ContactsContract.Groups.SOURCE_ID)
                append(" = ?")

                selectionArgs = arrayOf(id.id)
            }
        }
    }

    return withContactGroups(context, selection, selectionArgs) { it.firstOrNull() }
}

/**
 * 查询联系人群组信息
 * @param context 应用上下文
 * @param selection 查询条件（可为空）
 * @param selectionArgs 查询参数（可为空）
 * @param block 处理联系人群组信息序列的回调函数
 * @return 回调函数的返回值
 */
fun <R> withContactGroups(
    context: Context,
    selection: String? = null,
    selectionArgs: Array<String>? = null,
    block: (Sequence<ContactGroupInfo>) -> R,
): R {
    val cursor = context.contentResolver.query(
        ContactsContract.Groups.CONTENT_URI,
        CONTACT_GROUP_PROJECTION,
        selection,
        selectionArgs,
        null,
    ) ?: throw IOException("查询返回空游标")

    return cursor.use {
        val indexRowId = cursor.getColumnIndexOrThrow(ContactsContract.Groups._ID)
        val indexSourceId = cursor.getColumnIndexOrThrow(ContactsContract.Groups.SOURCE_ID)
        val indexTitle = cursor.getColumnIndexOrThrow(ContactsContract.Groups.TITLE)
        val indexAccountName = cursor.getColumnIndexOrThrow(ContactsContract.Groups.ACCOUNT_NAME)

        block(cursor.asSequence().map {
            ContactGroupInfo(
                cursor.getLong(indexRowId),
                cursor.getStringOrNull(indexSourceId),
                cursor.getString(indexTitle),
                cursor.getStringOrNull(indexAccountName),
            )
        })
    }
}